package com.mjf.reflection.question;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 为什么要用反射：通过配置文件，在不修改源代码的前提下，来控制程序（开闭原则）
 *
 * 反射相关的主要类：
 *  -java.lang.Class:代表一个类，Class对象表示某个类加载后在堆中的对象
 *  -java.lang.reflect.Method:代表一个类的方法，Method对象表示某个类的方法
 *  -java.lang.reflect.Field:代表类的成员变量，Field对象表示某个类的成员变量
 *  -java.lang.reflect.Constructor:代表类的构造方法，Constructor对象表示构造器
 *
 * 反射的优缺点：
 *  -优点：可以动态创建和使用对象（也是框架底层核心），使用灵活，没有反射机制，框架技术就失去底层支撑。
 *  -缺点：使用反射基本是解释执行，对执行速度有影响。
 */
public class ReflectQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException, InvocationTargetException {

        // 传统的方式 new 对象 -》 调用方法
/*        Cat cat = new Cat();
        // 如果要调用 eat(),需要修改源代码。如果是反射则只需要修改配置文件
        cat.hi(); */

        // 根据配置文件 re.properties 指定信息, 创建Cat对象并调用方法hi

        // 使用 Properties 类, 可以读写配置文件
        Properties properties = new Properties();
        String filePath = ReflectQuestion.class.getResource("/re.properties").getPath();
        properties.load(new FileInputStream(filePath));
        String classfullpath = properties.get("classfullpath").toString();  // com.mjf.reflection.Cat
        String methodName = properties.get("method").toString();    // hi
        System.out.println("classfullpath=" + classfullpath);
        System.out.println("method=" + methodName);

        // 创建对象,传统的方法行不通
        // new classfullpath();  // 不能 new 一个字符串

        // 使用反射机制解决
        // (1) 加载类, 返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
        // (2) 通过 cls 得到你加载的类 com.mjf.reflection.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass()); // 运行类型
        // (3) 通过 cls 得到你加载的类 com.mjf.reflection.Cat 的 methodName"hi"  的方法对象
        // 即：在反射中，可以把方法视为对象（万物皆对象）
        Method method1 = cls.getMethod(methodName);
        // (4) 通过 method1 调用方法: 即通过方法对象来实现调用方法
        System.out.println("=============================");
        method1.invoke(o); // 传统方法 对象.方法() , 反射机制 方法.invoke(对象)


        // java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量
//        Field nameField = cls.getField("name"); // 只能返回public修饰的 Field
        Field ageField = cls.getField("age");
        System.out.println(ageField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)

        // java.lang.reflect.Constructor: 代表类的构造方法, Constructor对象表示构造器
        Constructor constructor = cls.getConstructor(); // ()中可以指定构造器参数类型
        System.out.println(constructor);    // Cat() 无参构造器

        Constructor constructor2 = cls.getConstructor(String.class); // 传入形参的Class对象
        System.out.println(constructor2);   // Cat(String name) 有参构造器

    }
}
